package com.siyoumi.generator.serivce;

import com.siyoumi.component.XApp;
import com.siyoumi.exception.XException;
import com.siyoumi.generator.entity.ColumnEntity;
import com.siyoumi.generator.entity.TableEntity;
import com.siyoumi.generator.mapper.MapperDb;
import com.siyoumi.util.XReturn;
import com.siyoumi.util.XSqlStr;
import com.siyoumi.util.XStr;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class SysGeneratorSerivce {
    @Autowired
    private MapperDb mapperDb;

    @Value("${siyoumi.path-resource}")
    private String pathResource;

    /**
     * 生成文件内容
     *
     * @param tableNames
     * @return
     * @throws IOException
     */
    protected List<TableEntity> getTemplateList(String[] tableNames) throws IOException {
        List<TableEntity> listTable = new ArrayList<>();
        for (String tableName : tableNames) {
            TableEntity tb = mapperDb.tableOne(tableName);

            //表字段
            List<ColumnEntity> cols = mapperDb.tableColumns(tableName);
            tb.setColumns(cols);
            //表字段前缀
            ColumnEntity columnEntity = cols.get(1);
            String[] colNameArr = columnEntity.getColumnName().split("_");
            tb.setPrefix(colNameArr[0]);

            //类名
            String className = tableNameToClassName(tb.getTableName());
            tb.setClassName(className);

            listTable.add(tb);
        }

        String txtEntity = XApp.getFileContent("template/Entity.txt");
        String txtEntityBase = XApp.getFileContent("template/EntityBase.txt");
        String txtMapper = XApp.getFileContent("template/Mapper.txt");
        String txtService = XApp.getFileContent("template/Service.txt");
        String txtServiceImpl = XApp.getFileContent("template/ServiceImpl.txt");
        for (TableEntity entity : listTable) {
            String entityFileStr = txtEntity;
            String entityBaseFileStr = txtEntityBase;
            String mapperFileStr = txtMapper;
            String serviceFileStr = txtService;
            String serviceImplFileStr = txtServiceImpl;


            Map<String, String> replaceAll = new HashMap<>();
            replaceAll.put("{#tableName}", entity.getTableName());
            replaceAll.put("{#db}", entity.getDb());
            replaceAll.put("{#className}", entity.getClassName());
            replaceAll.put("{#prefix}", entity.getPrefix() + "_");

            StringBuilder columnsStr = new StringBuilder();
            for (ColumnEntity column : entity.getColumns()) {
                if (column.getColumnName().equals("id")) continue;

                if (column.getColumnName().equals(entity.getPrefix() + "_id")) {
                    //主键
                    columnsStr.append("\t@TableId(value = \"{#columnName}\", type = IdType.INPUT)\n");
                }
                if (column.getDataType().equals("datetime") || column.getDataType().equals("date")) {
                    //时间类型
                    //columnsStr.append("\t@JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n");
                    //columnsStr.append("\t@DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n");
                }
                columnsStr.append("\tprivate {#javaType} {#columnName};\n");

                columnsStr = new StringBuilder(columnsStr.toString().replace("{#columnName}", column.getColumnName()));
                columnsStr = new StringBuilder(columnsStr.toString().replace("{#javaType}", dbTypeToJavaType(column)));
            }
            replaceAll.put("{#columns}", columnsStr.toString());

            //包信息替换
            Map<String, String> packageInfo = dbToPackageInfo(entity.getDb(), entity.getTableName());
            replaceAll.put("{#packageEntity}", packageInfo.get("package_entity"));
            replaceAll.put("{#packageEntityBase}", packageInfo.get("package_entity_base"));
            replaceAll.put("{#packageMapper}", packageInfo.get("package_mapper"));
            replaceAll.put("{#packageService}", packageInfo.get("package_service"));
            replaceAll.put("{#packageServiceImpl}", packageInfo.get("package_service_impl"));


            entityFileStr = XStr.replaceByMap(entityFileStr, replaceAll);
            entityBaseFileStr = XStr.replaceByMap(entityBaseFileStr, replaceAll);
            //
            mapperFileStr = XStr.replaceByMap(mapperFileStr, replaceAll);
            serviceFileStr = XStr.replaceByMap(serviceFileStr, replaceAll);
            serviceImplFileStr = XStr.replaceByMap(serviceImplFileStr, replaceAll);


            entity.setFileStr(entityFileStr);
            entity.setFileStrEntityBase(entityBaseFileStr);
            entity.setFileStrMapper(mapperFileStr);
            entity.setFileStrService(serviceFileStr);
            entity.setFileStrServiceImpl(serviceImplFileStr);
        }

        return listTable;
    }

    public ByteArrayOutputStream gen(String[] tableNames) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        List<TableEntity> listTable = getTemplateList(tableNames);
        for (TableEntity entity : listTable) {
            //添加到zip
            zip.putNextEntry(new ZipEntry(XStr.concat("entity/", entity.getClassName(), ".java")));
            IOUtils.write(entity.getFileStr(), zip, "UTF-8");
            zip.putNextEntry(new ZipEntry(XStr.concat("entityBase/", entity.getClassName(), "Base.java")));
            IOUtils.write(entity.getFileStrEntityBase(), zip, "UTF-8");

            if (XStr.hasAnyText(entity.getFileStrMapper())) {
                //添加mapper
                zip.putNextEntry(new ZipEntry(XStr.concat("mapper/", entity.getClassName(), "Mapper.java")));
                IOUtils.write(entity.getFileStrMapper(), zip, "UTF-8");
            }
            if (XStr.hasAnyText(entity.getFileStrService())) {
                //添加service
                zip.putNextEntry(new ZipEntry(XStr.concat("service/", entity.getClassName(), "Service.java")));
                IOUtils.write(entity.getFileStrService(), zip, "UTF-8");
                //添加serviceImpl
                //zip.putNextEntry(new ZipEntry(XStr.concat("serviceImpl/", entity.getClassName(), "ServiceImpl.java")));
                //IOUtils.write(entity.getFileStrServiceImpl(), zip, "UTF-8");
            }
        }
        zip.closeEntry();
        IOUtils.closeQuietly(zip);

        return outputStream;
    }

    public XReturn genToJob(String[] tableNames) throws IOException {
        XReturn r = XReturn.getR(0);

        List<TableEntity> listTable = getTemplateList(tableNames);
        for (TableEntity entity : listTable) {
            Map<String, String> job = dbToPackageInfo(entity.getDb(), entity.getTableName());
            String path = XStr.concat(job.get("path_entity"), entity.getClassName(), ".java");
            String pathEntityBase = XStr.concat(job.get("path_entity_base"), entity.getClassName(), "Base.java");
            String pathMapper = XStr.concat(job.get("path_mapper"), entity.getClassName(), "Mapper.java");
            String pathService = XStr.concat(job.get("path_service"), entity.getClassName(), "Service.java");
            //String pathServiceImpl = XStr.concat(job.get("path_service_impl"), entity.getClassName(), "ServiceImpl.java");

            //entity
            genToJobFileSave(path, entity.getFileStr(), false);
            //entityBase
            genToJobFileSave(pathEntityBase, entity.getFileStrEntityBase(), true);

            //mapper service serviceimpl 文件不能覆蓋
            //mapper
            genToJobFileSave(pathMapper, entity.getFileStrMapper(), false);
            //service
            genToJobFileSave(pathService, entity.getFileStrService(), false);
            //serviceimpl
            //genToJobFileSave(pathServiceImpl, entity.getFileStrServiceImpl(), false);


            r.setData(entity.getTableName(), path);
        }

        return r;
    }

    private void genToJobFileSave(String filePath, String fileContent, Boolean overwrite) throws IOException {
        if (!overwrite) {
            //文件已存在，不能覆蓋
            File f = new File(filePath);
            if (f.exists()) {
                return;
            }
        }

        FileWriter fw = new FileWriter(filePath, false);
        fw.write(fileContent);
        fw.close();
    }


    /**
     * 表名转类名
     * <p>
     * t_s_app to sys_app
     * t_a_sheet to sys_sheet
     * </p>
     *
     * @param tableName 表名
     * @return 类名
     */
    public String tableNameToClassName(String tableName) {
        return XSqlStr.tableNameToClassName(tableName);
    }


    /**
     * 数据库类型转java类型
     *
     * @param columnEntity 数据库类型
     * @return java类型
     */
    public String dbTypeToJavaType(ColumnEntity columnEntity) {
        String javaType = "";
        switch (columnEntity.getDataType()) {
            case "varchar":
            case "char":
            case "mediumtext":
            case "enum":
            case "text":
            case "json":
                javaType = "String";
                break;
            case "bigint":
                javaType = "Long";
                break;
            case "int":
                javaType = "Integer";
                if (columnEntity.setUnsigned()) {
                    //设置unsigned，类型变成long
                    javaType = "Long";
                }
                break;
            case "tinyint":
                javaType = "Integer";
                break;
            case "decimal":
                javaType = "BigDecimal";
                break;
            case "datetime":
                javaType = "LocalDateTime";
                break;
            case "date":
                javaType = "LocalDate";
                break;
        }

        return javaType;
    }


    /**
     * 获取包信息及保存路径
     *
     * @param db 数据名称
     */
    public Map<String, String> dbToPackageInfo(String db, String td) {
        Map<String, String> data = new HashMap<>();
        switch (db) {
            case "wx_app":
            case "wx_app_x":
                String path = pathResource + "siyoumi-app\\src\\main\\java\\com\\siyoumi\\app\\";
                data.put("path", path);
                data.put("package", "com.siyoumi.app");
                break;
            default:
                throw new XException("error db:" + db);
        }

        switch (td) {
            case "t_s_account":
            case "t_s_accsuper_config":
            case "t_s_accsuper_zzz_app":
            case "t_s_app":
            case "t_s_app_router":
            case "t_sys_role":
            case "t_sys_role_router":
            case "t_s_log":
                String path = pathResource + "siyoumi-common\\src\\main\\java\\com\\siyoumi\\";
                data.put("path", path);
                data.put("package", "com.siyoumi");
                break;
        }

        String path = data.get("path");
        String packageStr = data.get("package");
        //path
        data.put("path_entity", XStr.concat(path, "entity\\"));
        data.put("path_entity_base", XStr.concat(path, "entity\\base\\"));
        data.put("path_mapper", XStr.concat(path, "mapper\\"));
        data.put("path_service", XStr.concat(path, "service\\"));
        data.put("path_service_impl", XStr.concat(path, "service\\impl\\"));
        //package
        data.put("package_entity", XStr.concat(packageStr, ".entity"));
        data.put("package_entity_base", XStr.concat(packageStr, ".entity.base"));
        data.put("package_mapper", XStr.concat(packageStr, ".mapper"));
        data.put("package_service", XStr.concat(packageStr, ".service"));
        data.put("package_service_impl", XStr.concat(packageStr, ".service.impl"));


        return data;
    }
}
